#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <cstring>
#include <signal.h>
#include "log.hpp"
#include "threadpool.hpp"
#include "Task.hpp"
#include "daemon.hpp"

#define MAXSIZE 4096

extern Log log;

enum
{
    PORT_ERR=1,
    SOCKET_ERR,    
    BIND_ERR,
    LISTER_ERR
};

int backlog = 10;

const uint16_t defaultport = 8080;
const std::string defaultip = "0.0.0.0";

class TCPServer;

class _ThreadData{
public:
    _ThreadData(const int sockfd, const std::string ip, const uint16_t port, TCPServer *t)
    :sockfd_(sockfd), ip_(ip), port_(port), tsvr_(t)
    {}
 
public:
    int sockfd_;
    std::string ip_;
    uint16_t port_;
    TCPServer *tsvr_;
};

class TCPServer{
public:
    TCPServer(const uint16_t serverport = defaultport, const std::string serverip = defaultip)
    : _listensockfd(0), _port(serverport), _ip(serverip)
    {
        if(_port < 1024){
            log(Warning, "Port number %d is too low, please use a port number > 1024", _port);
            exit(PORT_ERR);
        }
    }

    void InitServer(){
        // 1.创建tcp socket
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(_listensockfd < 0){
            log(Fatal, "server socket create error, listensockfd: %d, strerror: %s", _listensockfd, strerror(errno));
            exit(SOCKET_ERR);
        }
        log(INFO, "server socket create success, listensockfd: %d", _listensockfd);

        // 防止偶发性的服务器无法进行立即重启
        int opt = 1;
        setsockopt(_listensockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        inet_aton(_ip.c_str(), &(local.sin_addr));

        // 2.连接tcp socket
        if(bind(_listensockfd, (const struct sockaddr *)&local, sizeof(local)) < 0){
            log(Fatal, "server bind socket error, errno: %d, strerror: %s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        log(INFO, "server bind socket success, listensockfd: %d", _listensockfd);

        // 3.监听listen socket
        // TCP是面向链接的，服务器一般是比较"被动的"，服务器一直处于一种，一直在等待连接到来的状态
        if(listen(_listensockfd, backlog) < 0){
            log(Fatal, "server listen socket error, errno: %d, strerror: %s", errno, strerror(errno));
            exit(LISTER_ERR);
        }
        log(INFO, "server listen success, listensockfd: %d", _listensockfd);
    }

    static void *Routine(void *args){
        // 新线程使用线程分离将自己分离出去 主线程就不用再回收 直接继续创建其他的新线程
        pthread_detach(pthread_self());
        _ThreadData *td = static_cast<_ThreadData *>(args);

        // 线程的执行函数是静态的并不能直接访问类内方法 所以将this指针作为成员属性存放到线程信息对象中
        td->tsvr_->Service(td->sockfd_, td->ip_, td->port_);

        // 释放
        delete td;
        return nullptr;
    }

    void Run(){
        // 守护进程化
        Daemon();

        // 修改点2：防止因为客户端异常断开而导致服务器崩溃
        //signal(SIGPIPE, SIG_IGN);
        
        log(INFO, "TCPServer is running...");

        // 启动线程池
        ThreadPool<Task>::GetInstance()->start();

        while (true)
        {
            // 1.获取新链接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            // _listensockfd是用来监听的 后续的通信都是使用新的sockfd
            int sockfd = accept(_listensockfd, (struct sockaddr *)&client, &len);
            if(sockfd < 0){
                log(Warning, "server accept error, errno: %s, strerror: %s", errno, strerror(errno));
                continue;
            }

            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
            log(INFO, "server get a new link..., sockfd: %d, clientport: %d, clientip: %s", sockfd, clientport, clientip);

            // 2.根据新链接建立通信
            // version 1 --- 单进程版
            // Service(sockfd, clientip, clientport);
            // close(sockfd);


            // version 2 --- 多进程版
            // pid_t id = fork();
            // if(id == 0){
            //     // child
            //     close(_listensockfd);
            //     // 子进程再创建一个新的孙子进程 然后子进程立即退出
            //     if(fork() > 0){
            //         exit(0);
            //     }

            //     // grandson
            //     // 子进程退出后 孙子进程就没有了父进程 被系统领养 由系统进行回收
            //     Service(sockfd, clientip, clientport);
            //     close(sockfd);
            //     exit(0);
            // }
            // // father
            // // 父进程关闭sockfd 然后就只剩下子进程使用sockfd
            // close(sockfd);    
            // // 子进程创建孙子进程退出后，父进程立即回收子进程，然后继续进行下一次循环...
            // pid_t rid = waitpid(id, nullptr, 0); 


            // version 3 --- 多线程版本
            // pthread_t tid;
            // ThreadData *td = new ThreadData(sockfd, clientip, clientport, this);
            // pthread_create(&tid, nullptr, Routine, td);


            // version 4 --- 线程池版本
            Task t(sockfd, clientip, clientport);
            ThreadPool<Task>::GetInstance()->Push(t);

        }   
    }

    ~TCPServer(){
        if(_listensockfd > 0){
            close(_listensockfd);
        }
    }

private:
    void Service(int sockfd, const std::string &clientip, const uint16_t &clientport){
        // 测试代码
        char buffer[MAXSIZE];
        while(true){
            ssize_t n = read(sockfd, buffer, sizeof(buffer));
            if(n > 0){
                buffer[n] = 0;
                // 信息处理
                std::cout << "server get a message: " << buffer << std::endl;
                std::string echo_string = "tcpserver echo# ";
                echo_string += buffer;

                // 信息发送
                write(sockfd, echo_string.c_str(), echo_string.size());
            }
            else if(n == 0){
                log(INFO, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
                break;
            }
            else{
                log(Warning, "read error, sockfd: %d, clientip: %s, clientport: %d", sockfd, clientip.c_str(), clientport);
                break;
            }
        }
    }

private:
    int _listensockfd;
    uint16_t _port;
    std::string _ip;
};